package org.cjprods.icerupt;

import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.HashMap;

import com.hackoeur.jglm.Mat3;
import com.hackoeur.jglm.Mat4;
import com.hackoeur.jglm.Matrices;
import com.hackoeur.jglm.Vec;
import com.hackoeur.jglm.Vec3;

import org.lwjgl.BufferUtils;
import org.lwjgl.system.MathUtil;

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL12.*;
import static org.lwjgl.opengl.GL13.*;
import static org.lwjgl.opengl.GL14.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL21.*;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.opengl.GL31.*;
import static org.lwjgl.opengl.GL32.*;
import static org.lwjgl.opengl.GL33.*;

public class game
{
	int[] tex;
	int[] fbo;

	int fb_w, fb_h;
	float time = 0;
	float focal = 2.0f;
	float exposure = 0.3f;
	Vec3 camera_pos = new Vec3(0.0f, 0.0f, 1.0f);
	Vec3 camera_axis = new Vec3();
	static float speed = 0.05f;
	
	rotator gun;
	rotator sun;
	camera cam;
	bullet blt;
	
	program program_scene;
	program program_fxaa;
	program program_brightpass;
	program program_scatter;
	program program_gaussian;
	program program_composite;
	program program_display;

	//public game(int capacity) throws IOException
	public game() throws IOException
	{	
		cam = new camera();
		gun = new rotator(0, 44);
		sun = new rotator(-45, 89);
		blt = new bullet();
		
		
		tex = new int[4];
		fbo = new int[4];
		
		float points[] = new float[]{		// a full-screen quad
				-1, -1,    +1, -1,    -1, +1,
				+1, +1,    -1, +1,    +1, -1,
		};
		FloatBuffer raw_data = BufferUtils.createFloatBuffer(points.length);
		raw_data.put(points);
		raw_data.flip();
		
		glBindVertexArray(glGenVertexArrays());
		glBindBuffer(GL_ARRAY_BUFFER, glGenBuffers());
		glBufferData(GL_ARRAY_BUFFER, raw_data, GL_STATIC_DRAW);
		glEnableVertexAttribArray(0);
		glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);
		
		tex[0] = utils.create_texture(0, 6);
		tex[1] = utils.create_texture(1);
		tex[2] = utils.create_texture(2);
		tex[3] = utils.create_texture(3);

		fbo[0] = utils.create_framebuffer(tex[0]);
		fbo[1] = utils.create_framebuffer(tex[1]);
		fbo[2] = utils.create_framebuffer(tex[2]);
		fbo[3] = utils.create_framebuffer(tex[3]);
		
		program_scene = new program("scene");
		program_fxaa = new program("fxaa");
		program_brightpass = new program("brightpass");
		program_scatter= new program("scatter");
		program_gaussian = new program("gaussian");
		program_composite = new program("composite");
		program_display = new program("display");
	}
	
	public void update(float step, key_map button)
	{		
		//System.out.println("update:" + step);
		// pos
		if (button.get("w")) cam.zmove(-speed * 2);
		if (button.get("s")) cam.zmove(+speed * 2);
		if (button.get("a")) cam.xmove(-speed * 2);
		if (button.get("d")) cam.xmove(+speed * 2);
		//if (button.get("q")) cam.ymove(-speed);
		//if (button.get("e")) cam.ymove(+speed);
		
		// gun
		if (button.get("f")) sun.rotate(new Vec3(0.0f, +speed, 0.0f));
		if (button.get("h")) sun.rotate(new Vec3(0.0f, -speed, 0.0f));
		if (button.get("t")) sun.rotate(new Vec3(+speed, 0.0f, 0.0f));
		if (button.get("g")) sun.rotate(new Vec3(-speed, 0.0f, 0.0f));
		// target
		if (button.get("i")) cam.rotate(new Vec3(+speed / 4, 0.0f, 0.0f));
		if (button.get("k")) cam.rotate(new Vec3(-speed / 4, 0.0f, 0.0f));
		if (button.get("j")) cam.rotate(new Vec3(0.0f, +speed, 0.0f));
		if (button.get("l")) cam.rotate(new Vec3(0.0f, -speed, 0.0f));
		// focal
		if (button.get("o")) focal -= speed;
		if (button.get("u")) focal += speed;
		// exposure
		if (button.get("[")) exposure -= 0.1f * speed;
		if (button.get("]")) exposure += 0.1f * speed;
		// gun
		if (button.get("c")) gun.rotate(new Vec3(0.0f, +speed, 0.0f));
		if (button.get("n")) gun.rotate(new Vec3(0.0f, -speed, 0.0f));
		if (button.get("e")) gun.rotate(new Vec3(+speed, 0.0f, 0.0f));
		if (button.get("q")) gun.rotate(new Vec3(-speed, 0.0f, 0.0f)); 
		
		cam.update();
		sun.update();
		gun.update();
		blt.update(cam.pos());
		
		// shoot
		blt.shoot(gun.get(), cam.pos(), button.get("z"));
		
		
		
		program_scene.uniform("time", time);
		program_scene.uniform("focal", focal);
		program_scene.uniform("camera_axis", cam.get());
		program_scene.uniform("gun_force", blt.force());
		program_scene.uniform("gun_axis", gun.get());
		program_scene.uniform("sun_dir", sun.get().multiply((new Vec3(0.0f, 0.4f, -1.0f)).getUnitVector()));
		program_scene.uniform("player_pos", cam.pos());
		program_scene.uniform("camera_pos", cam.eye());
		program_scene.uniform("bullet_pos", blt.get());
		program_scene.uniform("bullet_age", blt.age());
		program_scene.uniform("bullet_size", blt.size());
		
		program_fxaa.uniform("scene", 2);

		program_brightpass.uniform("scene", 1);
		
		program_scatter.uniform("scene", 0);
		program_scatter.uniform("center", 0.0f, 0.0f);
		program_scatter.uniform("amount", 1.0f);
		
		program_gaussian.uniform("tex", 0);
		
		program_composite.uniform("blur", 0);
		program_composite.uniform("scene", 1);
		program_composite.uniform("scatter", 2);
		program_composite.uniform("exposure", exposure);
		
		program_display.uniform("scene", 3);
	
		
		time += step;
		
	}
	
	public void resize(int w, int h)
	{
		System.out.println("resize");
		program_scene.uniform("aspect_ratio", (float)w / (float)h);
		
		fb_w = w; fb_h = h;
		glViewport(0, 0, w, h);
		
		glActiveTexture(GL_TEXTURE0);
		utils.texture_resize(w, h);

		glActiveTexture(GL_TEXTURE1);
		utils.texture_resize(w, h);

		glActiveTexture(GL_TEXTURE2);
		utils.texture_resize(w, h);

		glActiveTexture(GL_TEXTURE3);
		utils.texture_resize(w, h);
	}
	
	public void render()
	{
		//System.out.println("render");
		
		program_scene.use();
		glBindFramebuffer(GL_FRAMEBUFFER, fbo[2]);
		glDrawArrays(GL_TRIANGLES, 0, 6);
		
		program_fxaa.use();
		glBindFramebuffer(GL_FRAMEBUFFER, fbo[1]);
		glDrawArrays(GL_TRIANGLES, 0, 6);
		
		
		program_brightpass.use();
		glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
		glDrawArrays(GL_TRIANGLES, 0, 6);
		glActiveTexture(GL_TEXTURE0);
		glGenerateMipmap(GL_TEXTURE_2D);
		
		
		program_scatter.use();
		//glBindFramebuffer(GL_FRAMEBUFFER, 0);
		glBindFramebuffer(GL_FRAMEBUFFER, fbo[2]);
		glDrawArrays(GL_TRIANGLES, 0, 6);
		
		
		program_gaussian.use();
		//glBindFramebuffer(GL_FRAMEBUFFER, 0);
		glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
		
		program_gaussian.uniform("dir", 1.0f, 0.0f);
		glDrawArrays(GL_TRIANGLES, 0, 6);
		glGenerateMipmap(GL_TEXTURE_2D);

		program_gaussian.uniform("dir", 0.0f, 1.0f);
		glDrawArrays(GL_TRIANGLES, 0, 6);
		glGenerateMipmap(GL_TEXTURE_2D);
		
		
		program_composite.use();
		//glBindFramebuffer(GL_FRAMEBUFFER, 0);
		glBindFramebuffer(GL_FRAMEBUFFER, fbo[3]);
		glDrawArrays(GL_TRIANGLES, 0, 6);
		glActiveTexture(GL_TEXTURE3);
		glGenerateMipmap(GL_TEXTURE_2D);
		
		
		program_display.use();
		glBindFramebuffer(GL_FRAMEBUFFER, 0);
		glDrawArrays(GL_TRIANGLES, 0, 6);
	}
}
